package com.yzjs.service.dataInfo.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bsnbase.sdk.client.fabric.FabricClient;
import com.bsnbase.sdk.entity.req.fabric.ReqKeyEscrow;
import com.bsnbase.sdk.entity.res.fabric.ResKeyEscrow;
import com.yzjs.common.base.service.BaseServiceImpl;
import com.yzjs.dao.chainDataInfo.ChainDataInfoMapper;
import com.yzjs.entity.chainDataInfo.ChainDataInfoEntity;
import com.yzjs.entity.dataInfo.DataInfoEntity;
import com.yzjs.dao.dataInfo.DataInfoMapper;
import com.yzjs.entity.vo.DataInfoVO;
import com.yzjs.service.dataInfo.DataInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Service("dataInfoService")
public class DataInfoServiceImpl extends BaseServiceImpl<DataInfoEntity, DataInfoMapper> implements DataInfoService {

    @Autowired
    private DataInfoMapper dataInfoMapper;
    @Autowired
    private ChainDataInfoMapper chainDataInfoMapper;

    private static final String UserName = "test001";
    private static final String ChainCode = "cc_app0009202110141743547938908_01";

    //数据解析
    public static DataInfoEntity getDataInfo(DataInfoVO vo) throws Exception {
        DataInfoEntity dataInfo = new DataInfoEntity();
        Map<Object, Object> uniqueKeysMap = new HashMap<>();
        String data = vo.getDataInfo();
        Map dataInfoMap = JSONObject.parseObject(data, Map.class);
        List<String> uniqueKeys = vo.getUniqueKeys();
        if (dataInfo == null || data.trim() == "")
            throw new Exception("dataInfo can not null！");
        if (!uniqueKeys.isEmpty()) {
            for (String uniqueKey : uniqueKeys) {
                Object o = dataInfoMap.get(uniqueKey);
                if (o == null) {
                    throw new Exception(uniqueKey + "，this key`s value can not null！");
                }
                uniqueKeysMap.put(uniqueKey, o);
            }
        }
        String dataSoleTagValue = JSON.toJSONString(uniqueKeysMap);

        //判断数据记录主键值是为空
        String pkValue = String.valueOf(dataInfoMap.get(vo.getPk()));
        if (pkValue == null || pkValue.trim() == "") {
            throw new Exception("pkValue can not null！");
        }
        //创建唯一标识
        StringBuilder identificationCode = new StringBuilder();
        identificationCode.append(vo.getDataFlag()).append("_");
        identificationCode.append(vo.getPk()).append("_");
        identificationCode.append(pkValue);
        //获取data的hash值
        String dataHash = String.valueOf(data.hashCode());
        //生成baseKey
        dataInfo.setBaseKey(String.valueOf(System.currentTimeMillis()));
        dataInfo.setData(data);//上链数据
        dataInfo.setDataPkTag(vo.getPk());//数据记录主键
        dataInfo.setDatasourceTag(vo.getDataFlag());//数据资源表标识
        dataInfo.setIdentificationCode(identificationCode.toString());//唯一标识
        dataInfo.setLocalHash(dataHash);//本地hash值
        dataInfo.setDataSoleTagValue(dataSoleTagValue);//数据记录唯一标识字段-值
        return dataInfo;
    }

    //获取查询信息
    public static Map<String, String> getSelectParam(Map<String, Object> params) throws Exception {
        Map<String, String> map = new HashMap<>();
        //数据源标识
        String dataFlag = (String) params.get("dataFlag");
        //数据记录主键
        String pk = (String) params.get("pk");
        //数据记录主键值
        String pkValue = String.valueOf(params.get("pkValue"));
        //数据唯一值 字段--值
        Map<String, String> uniqueKeys = (Map<String, String>) params.get("uniqueKeys");
        Set<Map.Entry<String, String>> entries = uniqueKeys.entrySet();
        if (!entries.isEmpty()) {
            //只去第一个的value作为查询条件
            for (Map.Entry<String, String> entry : entries) {
                map.put("uniqueKeyValue", entry.getValue());
                break;
            }
        }
        //创建唯一标识
        StringBuilder identificationCode = new StringBuilder();
        identificationCode.append(dataFlag).append("_");
        identificationCode.append(pk).append("_");
        identificationCode.append(pkValue);
        map.put("identificationCode", identificationCode.toString());
        return map;
    }

    @Override
    @Transactional(noRollbackForClassName = "RuntimeException")
    public DataInfoEntity dataUpload(DataInfoVO dataInfoVO) throws Exception {
        DataInfoEntity dataInfo = getDataInfo(dataInfoVO);
        ChainDataInfoEntity chainDataInfoEntity = new ChainDataInfoEntity();
        //判断是否存在数据
        DataInfoEntity old = dataInfoMapper.selectByIdentificationCode(dataInfo.getIdentificationCode());
        if (old == null) {
            //新增
            //数据本地入库
            dataInfoMapper.insert(dataInfo);
            chainDataInfoEntity.setBaseKey(dataInfo.getBaseKey());
            chainDataInfoEntity.setType(0);
            try {
                //上链操作
                ResKeyEscrow resKeyEscrow = doUpload(dataInfo);
                //修改状态为上链成功
                dataInfo.setStatus(1);
                //更新dataInfo记录
                dataInfoMapper.update(dataInfo);
                //设置操作成功
                chainDataInfoEntity.setIsSuccess(1);
                //设置TxId
                chainDataInfoEntity.setTxId(resKeyEscrow.getBlockInfo().getTxId());
            } catch (Exception e) {
                e.printStackTrace();
                //设置操作失败
                chainDataInfoEntity.setIsSuccess(0);
                //设置状态为 服务异常
                chainDataInfoEntity.setStatus(3);
                chainDataInfoEntity.setTxId(dataInfo.getBaseKey() + "数据上链服务异常,不返回txId,报错时间为:" + new Date());
                throw new RuntimeException("数据上链异常，错误信息：" + e.getMessage());
            } finally {
                //操作成功
                chainDataInfoMapper.insert(chainDataInfoEntity);
            }
        } else {
            //设置baseKey为old原数据的baseKey
            dataInfo.setBaseKey(old.getBaseKey());
            chainDataInfoEntity.setBaseKey(dataInfo.getBaseKey());
            chainDataInfoEntity.setType(1);
            //更新
            //判断是否data的hash值是否相同
            if (checkData(old.getLocalHash(), dataInfo.getLocalHash())) {
                chainDataInfoEntity.setIsSuccess(0);
                //相同 提示异常
                throw new RuntimeException("数据未发生变化,无需更新");
            }
            //不相同,做更新
            try {
                ResKeyEscrow resKeyEscrow = dataUpdate(dataInfo);
                //修改状态为更新成功
                dataInfo.setStatus(2);
                //设置操作状态为成功
                chainDataInfoEntity.setIsSuccess(1);
                chainDataInfoEntity.setTxId(resKeyEscrow.getBlockInfo().getTxId());
            } catch (Exception e) {
                chainDataInfoEntity.setIsSuccess(0);
                //设置状态为 服务异常
                chainDataInfoEntity.setStatus(3);
                chainDataInfoEntity.setTxId(dataInfo.getBaseKey() + "数据更新服务异常,不返回txId,报错时间为:" + new Date());
                //修改状态为更新失败
                dataInfo.setStatus(3);
                e.printStackTrace();
                throw new RuntimeException("数据链上异常，错误信息：" + e.getMessage());
            } finally {
                //更新dataInfo记录
                dataInfoMapper.update(dataInfo);
                //添加操作记录
                chainDataInfoMapper.insert(chainDataInfoEntity);
            }
        }
        return dataInfo;
    }

    @Override
    @Transactional(noRollbackForClassName = "RuntimeException")
    public DataInfoEntity dataDelete(DataInfoVO dataInfoVO) throws Exception {
        DataInfoEntity dataInfo = getDataInfo(dataInfoVO);
        //判断是否存在数据
        DataInfoEntity old = dataInfoMapper.selectByIdentificationCode(dataInfo.getIdentificationCode());
        if (old == null) {
            throw new RuntimeException("数据未上链,无法删除");
        }
        //判断数据是否一致
        Boolean flag = checkData(old.getLocalHash(), dataInfo.getLocalHash());
        if (!flag) {
            throw new RuntimeException("删除数据与已上链数据不一致,请检查数据再做删除");
        }
        ChainDataInfoEntity chainDataInfoEntity = new ChainDataInfoEntity();
        //同步BaseKey
        dataInfo.setBaseKey(old.getBaseKey());
        chainDataInfoEntity.setBaseKey(dataInfo.getBaseKey());
        //数据删除操作
        try {
            ResKeyEscrow resKeyEscrow = dataDelete(dataInfo.getBaseKey());
            chainDataInfoEntity.setType(3);
            //修改删除标记为已删除
            dataInfo.setDelFlag(1);
            dataInfoMapper.update(dataInfo);
            chainDataInfoEntity.setIsSuccess(1);
            chainDataInfoEntity.setTxId(resKeyEscrow.getBlockInfo().getTxId());
        } catch (Exception e) {
            chainDataInfoEntity.setIsSuccess(0);
            e.printStackTrace();
            //设置状态为 服务异常
            chainDataInfoEntity.setStatus(3);
            chainDataInfoEntity.setRemarks(e.getMessage());
            chainDataInfoEntity.setTxId(dataInfo.getBaseKey() + "链上数据删除服务器异常,不返回txId,报错时间为:" + new Date());
            throw new RuntimeException("链上数据删除异常，错误信息:" + e.getMessage());
        } finally {
            chainDataInfoMapper.insert(chainDataInfoEntity);
        }
        return dataInfo;
    }

    @Override
    @Transactional(noRollbackForClassName = "RuntimeException")
    public DataInfoEntity dataSelect(Map<String, Object> params) throws Exception {
        Map<String, String> paramMap = getSelectParam(params);
        //DataInfoEntity dataInfo = dataInfoMapper.selectByIdentificationCode(paramMap.get("identificationCode"));
        DataInfoEntity dataInfo = dataInfoMapper.selectByParams(paramMap.get("identificationCode"), paramMap.get("uniqueKeyValue"));
        if (dataInfo == null) {
            throw new RuntimeException("数据不存在,请确认查询条件");
        }
        ChainDataInfoEntity c = new ChainDataInfoEntity();
        c.setBaseKey(dataInfo.getBaseKey());
        c.setType(2);
        //根据baseKey查询信息
        try {
            ResKeyEscrow resKeyEscrow = dataSelect(dataInfo.getBaseKey());
            c.setIsSuccess(1);
            c.setTxId(resKeyEscrow.getBlockInfo().getTxId());
            //链上数据
            String chainData = resKeyEscrow.getCcRes().getCcData();
            String chainDataHash = String.valueOf(chainData.hashCode());
            //判断链上数据跟本地库数据是否一致
            if (!checkData(dataInfo.getLocalHash(), chainDataHash)) {
                //不一致,更新本地数据
                dataInfo.setData(chainData);
                dataInfo.setLocalHash(chainDataHash);
                //更新本地数据
                dataInfoMapper.update(dataInfo);
            }
        } catch (Exception e) {
            e.printStackTrace();
            c.setIsSuccess(0);
            //设置状态为 服务异常
            c.setStatus(3);
            c.setRemarks(e.getMessage());
            c.setTxId(dataInfo.getBaseKey() + "链上数据查询服务器异常,不返回txId,报错时间为:" + new Date());
            chainDataInfoMapper.insert(c);
            throw new RuntimeException("链上数据查询异常，错误信息：" + e.getMessage());
        }
        return dataInfo;
    }


    //数据上链
    public ResKeyEscrow doUpload(DataInfoEntity dataInfo) throws Exception {
        //初始化配置
        FabricClient.initConfig();
        ReqKeyEscrow reqKey = new ReqKeyEscrow();
        String[] args = {"{\"baseKey\":\"" + dataInfo.getBaseKey() + "\",\"baseValue\":" + JSON.toJSONString(dataInfo.getData()) + "}"};
        reqKey.setArgs(args);
        reqKey.setFuncName("set");
        reqKey.setChainCode(ChainCode);
        reqKey.setUserName(UserName);
        return FabricClient.reqChainCode(reqKey);
    }

    //数据更新
    public ResKeyEscrow dataUpdate(DataInfoEntity dataInfo) throws Exception {
        //初始化配置
        FabricClient.initConfig();
        ReqKeyEscrow reqKey = new ReqKeyEscrow();
        String[] args = {"{\"baseKey\":\"" + dataInfo.getBaseKey() + "\",\"baseValue\":" + JSON.toJSONString(dataInfo.getData()) + "}"};
        reqKey.setArgs(args);
        reqKey.setFuncName("update");
        reqKey.setChainCode(ChainCode);
        reqKey.setUserName(UserName);
        ResKeyEscrow resKeyEscrow = FabricClient.reqChainCode(reqKey);
        return resKeyEscrow;
    }

    //删除数据
    public ResKeyEscrow dataDelete(String baseKey) throws Exception {
        //初始化配置
        FabricClient.initConfig();
        ReqKeyEscrow reqKey = new ReqKeyEscrow();
        String[] args = {baseKey};
        reqKey.setArgs(args);
        reqKey.setFuncName("delete");
        reqKey.setChainCode(ChainCode);
        reqKey.setUserName(UserName);
        ResKeyEscrow resKeyEscrow = FabricClient.reqChainCode(reqKey);
        return resKeyEscrow;
    }

    //查询数据
    public ResKeyEscrow dataSelect(String baseKey) throws Exception {
        //初始化配置
        FabricClient.initConfig();
        ReqKeyEscrow reqKey = new ReqKeyEscrow();
        String[] args = {baseKey};
        reqKey.setArgs(args);
        reqKey.setFuncName("get");
        reqKey.setChainCode(ChainCode);
        reqKey.setUserName(UserName);
        ResKeyEscrow resKeyEscrow = FabricClient.reqChainCode(reqKey);
        return resKeyEscrow;
    }

    //判断数据是否发生变化
    public Boolean checkData(String oldHash, String nowHash) {
        if (oldHash.equals(String.valueOf(nowHash)))
            return true;
        return false;
    }
}